Computers and corresponding software applications are becoming increasingly sophisticated, enabling users to store, access, and manipulate data in a variety of ways. For instance, computer applications can be used to perform word processing, graphic design, audio/visual processing, data analytics, electronic communications, and much more.
Computer applications comprise code in the form of executable instructions that are executed by the processor(s) of a computer system. These instructions are created and sequenced together by a developer in a development environment. Once the instructions are compiled, interpreted, and/or built, the corresponding application can be deployed for use.
Unfortunately, computer systems and applications do not always operate in the manner that is intended or expected and which can result in an application crash, before or after deployment. For at least this reason, it is often necessary to debug an application to identify and fix the instructions in the code that can cause an application to crash.
Different developer tools have also been created to assist a developer in writing, editing, testing, and debugging an application's executable instructions to help mitigate system failures. Some of these tools include program code text editors, source code editors, debuggers, and integrated development environments (IDEs), just to name a few. Unfortunately, however, unnoticed bugs sometimes persist through the development stage and are not noticed until the software application has been deployed for general or widespread use. In circumstances where the application has already been deployed, debugging application failures can be more difficult, inasmuch as greater quantities of code must be examined to identify the instructions that are causing the problems.
Recently, a debugging process known as “time travel” debugging has emerged as a popular technique. With time travel debugging, a tracer program records the state of all the processor instructions and many of the states that exist during execution of a program. Later, when the program is debugged, the tracer data is used to recreate the exact memory and register states that exist at any point during execution, so as identify the specific instructions and locations in the code that caused the error. This technique has been particularly advantageous for developers in a development environment where high resource usage to collect the trace data is somewhat acceptable. However, it is still undesirable because it is so computationally expensive to log all of the data that is included in a heavy trace required for conventional time-travel debugging.
Additionally, it is noted that this type of heavy tracing for time-travel debugging is very impractical for most post deployment scenarios. Accordingly, most post-deployment debugging is notoriously difficult because developers have had to rely on very limited information (e.g., essentially only the information included in a memory dump, which is collected after deployment) as opposed to the vast amount of data collected for time travel debugging that is obtained during development. Even more particularly, it is noted that an application's entire execution history (e.g. time travel trace data) is usually not available because high-fidelity program tracing of those systems is prohibitively expensive (i.e. the client does not want to run a resource intensive debugging program in conjunction with the executing application). This limited availability of information is one of the primary factors as to why debugging already-deployed applications is so difficult.
Some attempts have been made to provide a developer with other data that can be used during debugging, such as client-side debugging data obtained from a sparse processor trace. For instance, Intel's® Processor Trace is a lightweight hardware tracing mechanism that is already included within almost all Intel® processors. A sparse hardware trace, which is also referred to herein as a sparse trace, a thin trace, a thin hardware trace, a sparse/thin trace or a sparse/thin hardware trace, identifies an application's control flow (i.e., a sequence of actual instructions that were executed by the computer system in response to an application's command). While the information can be useful to identify a general flow of the program, this control flow data only identifies the instructions that were executed. It does not describe the data values or even some of the memory addresses that were used. Consequently, use of this sparse data has not substantially improved conventional debugging techniques, particularly time-travel debugging.
Notwithstanding the foregoing, some attempts have been made to derive, or rather infer, the missing data values and missing memory addresses in the above-recited control flow information obtained from using the sparse hardware processor trace. Generally, these attempts combine the state values included within a memory dump with the lightweight hardware tracing data (i.e. the control flow) and then perform a recursive backward analysis to derive the unknown states corresponding to each processor instruction. While some of these debugging techniques have been helpful, these techniques have not been widely adopted because they successfully identify only a small fraction of the total number of processor instruction states.
Coupled with this low performance, existing attempts to debug a program with sparse hardware trace data have encountered significant problems that limit their scalability and practicality. One such problem, for example, is referred to herein as “memory aliasing.” This type of problem occurs when a write instruction for an unknown destination address is encountered during the iterative backward inference analysis and the debugger fails to know how to handle state values for that unknown destination address. To address this problem, some existing proposals include making an attempt to infer all of the possible options for all unknown memory locations. However, this type of approach is pragmatically prohibitive because the debugging systems are unable to track and solve all possible inferences for all branches without reaching a critical failure of resources, particularly for large and complex programs. Another approach is to refrain from making any assumptions for any unknown memory locations and corresponding address values. However, this naïve approach is impractical because it effectively prevents the debugging software from identifying/inferring any significant quantity of information, particularly for complex programs. In this regard, both of the foregoing approaches are inadequate and are effectively unscalable. This is particularly true when using sparse/thin hardware trace data to debug programs that have executed large quantities of instructions (e.g., thousands of instructions, tens of thousands of instructions, hundreds of thousands of instructions, or even millions of instructions).
Another problem with time-travel debugging based on sparse hardware trace data is a “concurrency” problem that exists when an application executes on multiple processors and/or with multiple threads that are traced separately. The problem specifically relates to the difficulty in knowing the sequence of instructions, since the different instructions for each trace are not each time stamped. Instead, blocks of instructions are associated with a single timestamp within each of the different traces. Using the block timestamp, it is possible to identify the starting time of each block, but not the specific times and relative sequence of each instruction in the blocks of instructions, particularly when those blocks of instructions executed concurrently with blocks of instructions executing on other threads. Existing debugging systems are simply unable to handle this concurrency problem because of the potential for exponential branching/analysis that would be required to infer all possible sequencing options.
In view of the foregoing, it will be appreciated that there is an existing need to identify debugging solutions that mitigate memory aliasing and/or multi-thread concurrency problems that result from time-travel debugging with sparse/thin hardware traces and, particularly, solutions that can be effectively scaled.
The subject matter claimed herein is not limited to embodiments that solve any disadvantages or that operate only in environments such as those described above. Rather, this background is only provided to illustrate one exemplary technology area where some embodiments described herein may be practiced.